/**
 * Model package.
 */
package fr.umlv.m2.ig.model;

import java.awt.Point;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * This class defines a rail network station.
 * 
 * @author Gambouele Bertrand.
 * @author Viravout Alexandre.
 */
public class Station {

	/**
	 * The name of the station.
	 */
	private final String name;
    
  /**
   * The type of the station.
   */
	private final StationType type;

	/**
	 * Map of the station coordinates for each line serving the station.
	 */
	private final Map<String, Point> coordinates;

  /**
   * Station state for each line.
   */
	private final Map<String, StationState> states;

	/**
	 * Map of next stations for each destination in each line serving the
	 * station.
	 */
	private final Map<String, Map<String, String>> nextStations;

	/**
	 * Map of station state (busy or not busy).
	 */
	private final Map<String, Map<String, Boolean>> isBusy;

	/**
	 * waiting times on the station for each line which serves it.
	 */
	private Map<String, Double> waitingTimes;

	/**
	 * Creates a {@code Station} object. This constructor is package visible.
	 * Use the class {@link StationOrCurveBuilder} to create a new object.
	 * 
	 * @param name the station name.
	 * @param type the station type.
	 */
	Station(final String name, final StationType type) {
		this.name = name;
		this.type = type;
		this.states = new HashMap<String, StationState>();
		this.coordinates = new HashMap<String, Point>();
		this.nextStations = new HashMap<String, Map<String, String>>();
		this.waitingTimes = new HashMap<String, Double>();
		this.isBusy = new HashMap<String, Map<String, Boolean>>();
	}

	/**
	 * Gets the name of the station.
	 * 
	 * @return the station name.
	 */
	public String getName() {
		return this.name;
	}

	/**
	 * Gets station state for the specified line.
	 * 
	 * @param line the line name.
	 * @return station state.
	 */
	public StationState getState(String line) {
		return this.states.get(line);
	}

	/**
	 * Gets the type of the station.
	 * 
	 * @return the type.
	 */
	public StationType getType() {
		return this.type;
	}

	/**
	 * Gets coordinates of the station for the specified line.
	 * 
	 * @param lineName the line name.
	 * @return the station coordinates for the specified line.
	 */
	public Point getCoordinates(final String lineName) {
		return this.coordinates.get(lineName);
	}

	/**
	 * Gets the name of the next station 
	 * for the specified destination in the specified line.
	 * 
	 * @param lineName the line name.
	 * @param destination station name.
	 * @return the name of the next station.
	 */
	public String getNextStation(final String lineName, final String destination) {
		Map<String, String> nextStation = this.nextStations.get(lineName);

		if (nextStation == null) {
			return null;
		}

		return nextStation.get(destination);
	}

	/**
	 * Sets the station coordinates for the specified line.
	 * 
	 * @param lineName the line name.
	 * @param coord the station coordinates.
	 */
	void setCoordinates(final String lineName, final Point coord) {
		this.coordinates.put(lineName, coord);
		this.states.put(lineName, StationState.OPENED);
	}

	/**
	 * Adds a new edge in the station for the specified line.
	 * 
	 * @param lineName the line name.
	 * @param destination the destination station name.
	 * @param nextStationName the next station name.
	 */
	void addEdge(final String lineName, final String destination, final String nextStationName) {
		Map<String, String> nextStation = this.nextStations.get(lineName);

		if (nextStation == null) {
		  nextStation = new HashMap<String, String>();
			this.nextStations.put(lineName, nextStation);
		}

		nextStation.put(destination, nextStationName);
	}

	/**
	 * Gets a collections of lines names for this station.
	 * 
	 * @return A collection of {@code String}.
	 */
	public Collection<String> getLines() {
		return Collections.unmodifiableCollection(coordinates.keySet());
	}

	/**
	 * Gets a collections of next stations for this station 
	 * in the specified line.
	 * 
	 * @param lineName the line name.
	 * @return a collection of {@code String}.
	 */
	public Collection<String> getAllNextStations(final String lineName) {
		Map<String, String> edges = nextStations.get(lineName);

		if (edges == null) {
			return Collections.emptySet();
		}

		return Collections.unmodifiableCollection(edges.values());
	}

	/**
	 * Gets the number of lines which serves this station.
	 * 
	 * @return number of lines serving the station.
	 */
	public int countLines() {
		return this.nextStations.size();
	}

	/**
	 * Returns true if the station is served by the specified line or false
	 * otherwise.
	 * 
	 * @param line the concerned line.
	 * @return true if the specified line serves the station. False otherwise.
	 */
	public boolean isServed(final String line) {
		return this.nextStations.containsKey(line);
	}

	/**
	 * Changes the station of the station for the specified line.
	 * 
	 * @param line the line where station state will change.
	 */
	void changeState(final String line) {
		StationState state = this.states.get(line);
		
		if (state != null) {
			StationState newState = ((state == StationState.OPENED) ? StationState.CLOSED : StationState.OPENED);
			this.states.put(line, newState);
		}
	}

	/**
	 * Sets if the station is busy or not.
	 * 
	 * @param line line name.
	 * @param destination concerned destination.
	 * @param busy a boolean indicating if the station is busy.
	 */
	void setBusy(final String line, final String destination, final boolean busy) {
		Map<String, Boolean> isBusy2 = this.isBusy.get(line);

		if (isBusy2 == null) {
			isBusy2 = new HashMap<String, Boolean>();
			this.isBusy.put(line, isBusy2);
		}

		isBusy2.put(destination, busy);
	}
	
	/**
	 * Returns true if the station is busy. Otherwise, returns false.
	 * 
	 * @param line the line name.
	 * @param destination the destination on the dock.
	 * @return true if the station is busy or false otherwise.
	 */
	public boolean isBusy(final String line, final String destination) {
		Map<String, Boolean> isBusy = this.isBusy.get(line);

		if (isBusy == null) {
			return false;
		}
		
		Boolean tmp = isBusy.get(destination);

		return (tmp == null) ? false : tmp;
	}
	
	/**
	 * Sets a station waiting time.
	 * 
	 * @param stationName the station to set.
	 * @param time the time to wait.
	 */
	void setWaitingTime(String lineName, Double time) {
	  this.waitingTimes.put(lineName, time);
	}

	/**
	 * Returns station waiting time for the specified line.
	 * 
	 * @param line the concerned line.
	 * @return the waiting time.
	 */
	double getWaitingTime(String line) {
		return this.waitingTimes.get(line);
	}
}
